package futbol.tacticas.concursantes2007.shalafisboys;

import futbol.tacticas.concursantes.Tactica;

import java.awt.Color;
import java.awt.Point;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Vector;

import futbol.General;
import futbol.tacticas.*;


public class TacticaShalafi extends TacticaShalafiBase {
	
	private final String NOMBRE = "Shalafi's boys";
	private boolean porteroDefiende = false;
	private int parcial = -1;
	
	public TacticaShalafi()
	{
		super(70,4);
		// Creamos a los jugadores
		jugadores = new Vector<ShalJugador>();
		ShalJugador jug = new ShalJugador(0,"Benji Price",5, -160,0,this);
		jug.setTipoMovimiento(ShalJugador.TipoMovimiento.PivoteZonaRadial,40);
		jug.setSeleccionDisparo(ShalJugador.TipoSeleccionTiro.Heuristica);
		jugadores.add(0,jug);
		jug = new ShalJugador(1,"Ted Carter",4,-90,20,this);
		jug.setTipoMovimiento(ShalJugador.TipoMovimiento.Antigravedad,50, false,false);
		jugadores.add(1,jug);
		jug = new ShalJugador(2,"Bruce Harper",4,-100,-30,this);
		jug.setTipoMovimiento(ShalJugador.TipoMovimiento.Antigravedad,50, false,false);
		jugadores.add(2,jug);
		jug = new ShalJugador(3,"Clifford Yuma",5,-80,-60,this);
		jug.setTipoMovimiento(ShalJugador.TipoMovimiento.Antigravedad,50, false,false);
		jugadores.add(3,jug);
		jug = new ShalJugador(4,"Jason Derrick",4,-90,70,this);
		jug.setTipoMovimiento(ShalJugador.TipoMovimiento.Antigravedad,60);
		jugadores.add(4,jug);
		// Fin de la defensa
		jug = new ShalJugador (5,"Tom Baker",4,10,50,this);
		jug.setTipoMovimiento(ShalJugador.TipoMovimiento.Antigravedad,60,true,false);
		jugadores.add(5,jug);
		jug = new ShalJugador (6,"Dani Melow",6,0,0,this);
		jug.setTipoMovimiento(ShalJugador.TipoMovimiento.Orbital,40,true,false);
		jugadores.add(6,jug);
		// Lateral derecho
		jug = new ShalJugador (7,"James Derrick",5,60,-10,this);
		jug.setTipoMovimiento(ShalJugador.TipoMovimiento.Antigravedad,100,true,false);
		jugadores.add(7,jug);
		// Lateeral Izquierdo
		jug = new ShalJugador (8,"Julien Ross",7,90,60,this);
		jug.setTipoMovimiento(ShalJugador.TipoMovimiento.Antigravedad,100,true,false);
		jugadores.add(8,jug);
		// Media punta
		jug = new ShalJugador(9,"Oliver Aton",6,110,0,this);
		jug.setTipoMovimiento(ShalJugador.TipoMovimiento.Artillero,60,true,false);
		jugadores.add(9,jug);
		// Delantero centro
		jug = new ShalJugador (10,"Marc Lendels",7,80,-60,this);
		jug.setTipoMovimiento(ShalJugador.TipoMovimiento.Antigravedad,100,true,false);
		//jug.setTipoMovimiento(ShalJugador.TipoMovimiento.Artillero,60,true,true);
		jugadores.add(10,jug);
	}
	
	public Color getColor1() {
		return Color.GREEN;
	}

	public Color getColor2() {
		return Color.red;
	}

	public Color getColorNumero() {
		return Color.white;
	}

	public String getNombre() {		
		return NOMBRE;
	}

	@SuppressWarnings("unchecked")
	protected void seleccionTacticaJugadores() {
		estructurarDefensa();
		validarEstadisticas();
	}
	
	public void onGol(int gol) {
		super.onGol(gol);
		if (gol == -1)
		{
			// Si nos meten el segundo nos lo pensamos
			if (this.marcadorOponente == 2)
			{
				// Si nos lo marcan pronto, o si la diferencia es pequenia
				 if (this.turnos < 30 || this.marcadorEquipo < 4)
				 {
						numDefensas = 5;
						this.minPeligro -=10;
				 }
			}
			if (marcadorOponente == 4)
			{
				// Poner mas gente a defender
				if (numDefensas == 4)
				{
					numDefensas = 5;
				}
				 for (int i=1;i<5;i++)
						jugadores.get(i).setSeleccionDisparo(ShalJugador.TipoSeleccionTiro.Completo);

			}
			if (marcadorOponente == 6)
			{
				for (int i=1;i<5;i++)
					jugadores.get(i).setSeleccionDisparo(ShalJugador.TipoSeleccionTiro.OpuestoMedioCampo);
			}
			// Tambien si nos adelantan avanzado el partido
			if (getVentaja() <= 0 && turnos > 60)
			{				
				// Poner mas gente a defender
				if (numDefensas == 4)
				{
					numDefensas = 5;
				}
				else if (!porteroDefiende && (marcadorOponente > 4 && turnos > 900))
				{
					numDefensas = 6;
					porteroDefiende  = true;
				}
			}
		}
		else if (getVentaja() >0 && porteroDefiende)
		{
			numDefensas = 5;
			porteroDefiende =false;
		}
	}
	
	protected void validarEstadisticas() {
		// AL cabo del primer parcial, cambiamos la estrategia
		if (turnos >= 400 + marcadorEquipo*50 && parcial == -1 && getVentaja()<3)
		{ 
			parcial = marcadorEquipo;
			setEstrategia(2);
		}
		// Al cabo del segundo parcial, escogemos la mejor
		if (turnos == 800 + marcadorEquipo*50 )
		{
			if (parcial*2 > marcadorEquipo)
				// Volver a la estrategia inicial
				setEstrategia(1);
		}
		// Segun nos haya ido la primera mitad, el ultimo tramos cambiamos
		if (turnos == 900)
		{
			if (marcadorEquipo < 6)
			for (int i=5;i<10;i++)
				jugadores.get(i).setSeleccionDisparo(ShalJugador.TipoSeleccionTiro.Completo);
		}
	}

	protected void setEstrategia(int num) {
		if (num == 1)
		{
			// 9 y 11, laterales otra vez
			jugadores.get(10).setTipoMovimiento(ShalJugador.TipoMovimiento.Antigravedad,100,true,true);
			jugadores.get(10).setPosicionInicial(80,-60);
			jugadores.get(8).setTipoMovimiento(ShalJugador.TipoMovimiento.Antigravedad,50,true,true);
			jugadores.get(8).setPosicionInicial(90,60);
			// 7 y 8 organizando
			jugadores.get(7).setTipoMovimiento(ShalJugador.TipoMovimiento.Antigravedad,100,true,false);
			jugadores.get(7).setPosicionInicial(60,-10);
			jugadores.get(6).setTipoMovimiento(ShalJugador.TipoMovimiento.Orbital,40,true,false);
			jugadores.get(6).setPosicionInicial(0,0);
		}
		else if (num == 2)
		{
			// Al 11 lo ponemos de centro
			jugadores.get(10).setTipoMovimiento(ShalJugador.TipoMovimiento.Artillero,100,true,true);
			jugadores.get(10).setPosicionInicial(50,10);
			// Al 9, de organizador
			jugadores.get(8).setTipoMovimiento(ShalJugador.TipoMovimiento.Orbital,40,true,true);
			jugadores.get(8).setPosicionInicial(0,0);
			// 7 y 8 de laterales
			jugadores.get(7).setTipoMovimiento(ShalJugador.TipoMovimiento.Antigravedad,100,true,false);
			jugadores.get(7).setPosicionInicial(90,-60);
			jugadores.get(6).setTipoMovimiento(ShalJugador.TipoMovimiento.Antigravedad,100,true,false);
			jugadores.get(6).setPosicionInicial(80,60);
		}
	}

	@SuppressWarnings("unchecked")
	protected void estructurarDefensa() {
		// Buscamos a los dos oponentes mas peligrosos (fuerza 7)
		Collections.sort(ordenados);
		// Miramos quien les cubre
		for (int i=0;i<numDefensas;i++)
		{
			if (porteroDefiende)
				defensas.add(jugadores.get(i));
			else
				defensas.add(jugadores.get(i+1));
		}
		int i;
		for (i=0;i<numDefensas;i++)	{
			// Tomamos los mas peligroso
			if (ordenados.get(i).getPeligro()<minPeligro)	break;
			else{
				// Comprobar si esta bien cubierto, si es que si, que siga el mismo
				if (ordenados.get(i).estaBienCubierto())
					defensas.remove(ordenados.get(i).getQuienCubre());
				else // Sino, que lo cubra el mas cercano
				{
					//Ordenamos por cercania al oponente
					Collections.sort((List) defensas,new DistanceComparator(ordenados.get(i)));
					defensas.get(0).setTipoMovimiento(ShalJugador.TipoMovimiento.Cubrir,ordenados.get(i));
					defensas.remove(0);
				}
			}
		}
		// Segun el numero de oponenetes cubiertos y de defensas que nos queden
		if (!defensas.isEmpty()) {
			//	Los defensas que quedan, que pivoten sobre el oponente, pero sin cubrir
			for (int j=0;j<defensas.size();j++)
			{
				if (j%2==0)
					// 	Segundo defensa sobre el mas peligroso
					defensas.get(j).setTipoMovimiento(ShalJugador.TipoMovimiento.Cubrir,ordenados.get(j/2));
				else
					defensas.get(j).setTipoMovimiento(ShalJugador.TipoMovimiento.Pivote,ordenados.get(i+j/2));
			}
		}
		defensas.removeAllElements();
	}
}


abstract class TacticaShalafiBase implements Tactica {
	
	public static final Point poste1 = new Point (-160,30);
	public static final Point poste2 = new Point (-160,-30);
	public static final Point posteOp1 = new Point (160,30);
	public static final Point posteOp2 = new Point (160,-30);
	public static final Point centroAriba = new Point (0,-120);
	public static final Point centroAbajo = new Point (0,120);
	public static final Point centro = new Point (0,0);
	public static final Point porteriaOponente = new Point (160,0);
	public static final Point porteria = new Point (-160,0);	
	
	protected double minPeligro;
	protected int numDefensas;
	
	protected int[] alcanceMin;
	protected int[] alcanceMax;
	protected ShalBola bola;
	protected boolean[] vaPorLaBola;
	protected int cuandoAlcanzaBola;
	protected int marcadorEquipo, marcadorOponente;
	protected int turnos;
	protected SituacionJugadores sj;
	protected Vector<ShalJugador> jugadores, defensas;
	protected Vector<ShalOponente> oponentes, ordenados;
	protected boolean comprobarVelocidad;

	public TacticaShalafiBase(int minPeligro, int numDefensas) 
	{
		this.numDefensas = numDefensas;
		this.minPeligro = minPeligro;
		
		comprobarVelocidad = true;
		marcadorEquipo = 0;
		marcadorOponente = 0;
		turnos = 0;
		vaPorLaBola = new boolean[11];
		oponentes = new Vector<ShalOponente>();
		ordenados = new Vector<ShalOponente>();
		jugadores = new Vector<ShalJugador>();
		defensas = new Vector<ShalJugador>();
		for (int i=0;i<11;i++)
		{
			oponentes.add(new ShalOponente(i,this));
			ordenados.add(oponentes.get(i));
		}
		// Inicializacion de los radios de alcance
		alcanceMin = new int[8];
		alcanceMax = new int[8];
		for (int i=0;i<8;i++)
		{
			alcanceMax[i] = i*3*(i*3+1)/2;
			alcanceMin[i] = Math.max(0,alcanceMax[i]-100);
		}
		bola = new ShalBola(this);
	}

	public Vector<Comando> getComandos(SituacionJugadores sj) {	
		this.sj = sj;
		Vector<Comando> comandos = new Vector<Comando>();
		// Actaulizamos
		int esGol = bola.esGol();
		bola.setNuevoPunto(sj.getBola());
		calcularQuienVaPorLaBola();
		// Actualizar oponentes
		boolean aBool = false, bTemp = false;
		if ((comprobarVelocidad || turnos < 10) && esGol == 0) aBool = true;
		for (int i=0;i<11;i++)
		{
			if (oponentes.elementAt(i).update(sj.getContrario()[i],aBool)) 
				bTemp = true;
		}
		if (bTemp == false) comprobarVelocidad = false;
		//System.out.println(bTemp+"- "+turnos);
		seleccionTacticaJugadores();
		// Ahora buscamos los comandos adecuados
		for (int i=0;i<11;i++)
		{
			ShalJugador jugador = jugadores.get(i);
			jugador.update(sj.getMisJugadores()[i]);
			jugador.addComandos(comandos);
		}
		turnos++;
		return comandos;
	}

	protected abstract  void seleccionTacticaJugadores();

	public int getFuerza(int n) {
		return jugadores.get(n).getFuerza();
	}
	
	public int getVelocidad(int numero) {
		return getJugador(numero).getVelocidad();
	}
	
	public int getXInicial(int n) {		
		return jugadores.get(n).getXInicial();
	}

	public int getYInicial(int n) {
		return jugadores.get(n).getYInicial();
	}

	public ShalJugador getJugador(int i) {
		return this.jugadores.get(i);
	}

	public ShalBola getBola() {
		return bola;
	}

	private void calcularQuienVaPorLaBola() {
		// Calculamos el primer tiempo en el que la bola es golpeable
		int t0 =  Math.max(1,15 - bola.getVelocidad());
		// Ahora, para cada t, buscamos a un jugador que pueda llegar a la bola
		int t, temp = 0, mejor = sj.getMasCercanoDeBola();
		boolean salir = false;
		for (t=t0;!salir;t++)
		{
			for (int i=0;i<11;i++)
			{
				ShalJugador jug = getJugador(i);
				if (jug.canReach(bola,t) )	
				{
					if (t<4) {
						vaPorLaBola[i] = true;
						salir = true;
					}
					else {
						temp = jug.getFuerza();
						if (jug.getFuerza() > temp)	{
							mejor = i;
							temp = jug.getFuerza();
						}
					}
				}
				if (temp >0) salir = true;
			}
		}
		vaPorLaBola[mejor] = true;
		if (!salir) t = 0;
		cuandoAlcanzaBola = t;
	}

	public boolean voyPorLaBola(int numero) {
		return vaPorLaBola[numero];
	}

	public ShalJugador getEquipo(int num) {
		return getJugador(num);
	}

	public ShalOponente getOponente(int num) {
		return oponentes.elementAt(num);
	}

	public int getAlcanceMax(int i) {
		return alcanceMax[i];
	}

	public int getAlcanceMin(int i) {
		return alcanceMin[i];
	}

	public Point getPuntoEncuentroBola() {
		// Sabemos dentro de cuanto tiempo, estimamos la bola
		return bola.estimaPosicionFutura(cuandoAlcanzaBola);
	}
	
	double getHeuristicaOponentes(int t, Point dest) {
		double sum = 0;
		for (int i=0;i<11;i++)
			sum += oponentes.elementAt(i).getHeuristica(t,dest);
		return sum;
	}

	public double getPeligroOponente(int oponente) {
		return oponentes.elementAt(oponente).getPeligro();
	}

	public void onGol(int gol) {
		if (gol==1) marcadorEquipo++;
		else if (gol==-1) marcadorOponente++;
		for (int i=0;i<11;i++) 
			vaPorLaBola[i] = false;
	}

	public void onBolaGolpeada(int velocidad, double angulo) {
		for (int i=0;i<11;i++) 
			vaPorLaBola[i] = false;
	}


	public int getVentaja() {
		return this.marcadorEquipo-this.marcadorOponente;
	}


	public ShalOponente getContrarioMasCerca(ShalJugador jugador) {
		return oponentes.get(sj.getContrarioMasCerca(jugador.getNumber()));
	}

}

abstract class ShalJugadorBase extends Point implements Comparable{

	public ShalJugadorBase(TacticaShalafiBase p, int i, int fuerza) {
		this.number = i;
		this.fuerza = fuerza;
		parent = p;
	}
	
	protected int number;
	protected int fuerza;
	double peligro;
	public Point porteria, porteriaOponente;
	protected TacticaShalafiBase parent;

	public final int getVelocidad() {		
		return 10-fuerza;
	}

	public final int getFuerza() {		
		return fuerza;
	}

	public double getAlcanceMaximo()
	{
		return parent.getAlcanceMax(getFuerza());
	}
	
	public double getAlcanceMinimo()
	{
		return parent.getAlcanceMin(getFuerza());
	}

	public boolean canReach(ShalBola bola, int t) {
		return super.distance(bola.estimaPosicionFutura(t)) < (getVelocidad()*t+10);
	}

	public double getPeligro() {
		return peligro;
	}

	public int compareTo(Object o) {
		int temp = (int) -(peligro - ((ShalJugadorBase)o).getPeligro());
		if (temp ==0) temp = (int) (x-((ShalJugadorBase)o).getX());
		return temp;
	}
	
	public int getNumber() {
		return number;
	}
	
	protected abstract double calculaPeligro ();
	protected abstract double calculaPeligro (Point p);
	
	protected double calculaPeligro(double dp) 
	{
		double am = Math.max(getAlcanceMinimo(),40);
		double ret;
		
		if (dp < am)
			ret = 100 + getFuerza();
		else if (dp < getAlcanceMaximo())
			// Proporcional
			ret = 100 - (dp-am)/getVelocidad();
		else
			// Minimo
			ret = 100 -(getAlcanceMaximo()-am)/getVelocidad();
		return ret;
	}

	/**
	 * 
	 * Tiempo minimo que tarda la bola en llegar hasta donde esta el jugador
	 * 
	 * @return
	 */
	public double getTiempoMinBola() 
	{
		double d = distance(parent.getBola());
		for (int t=21;t>0;t--)
		{
			d-=t;
			if (d<0) return t;
		}
		return 30;
	}

	/**
	 * 
	 * Valor heuristico de un jugador en concreto, si puede llegar a por la bola
	 * sera el valor del peligro en esa posicion, sino, sera 0
	 * 
	 * @param t tiempo en el futuro en el que calcular la heuristica
	 * @param dest punto en el que se encontrara la bola
	 * @return
	 */
	protected double getHeuristica(int t, Point dest) {
		// Si lo puedo alcanzar en un tiempo t, todo lo que me sobra es el valor
		double reach = (int) (distance(dest) / getVelocidad());
		if (reach > t) return calculaPeligro(dest);
		else return 0;
	}
}

@SuppressWarnings("serial") 
class ShalBola extends Point 
{
	
	private Point anterior = new Point(0,0);
	private TacticaShalafiBase listener;;
	private int velocidad;
	private double angulo;

	public ShalBola (TacticaShalafiBase parent)
	{
		this.listener = parent;
	}

	public void setNuevoPunto(Point bola)
	{
		// Comprabomos si es donde deberia
		double d = bola.distance(this.estimaPosicionFutura(1));
		anterior.setLocation(this);
		this.setLocation(bola);		
		if (esGol()!=0)
		{
			listener.onGol(esGol());
			velocidad = 0;
		}
		else
		{
			// Calculo de la velocidad (uno menos que la anterior
			velocidad = (int) super.distance(anterior)-1;
			angulo = General.getAngulo(anterior.getX(),anterior.getY(),getX(),getY());
		}
		if (d>5)
			listener.onBolaGolpeada(velocidad,angulo);
	}
	
	public int getVelocidad() {
		return velocidad;
	}

	public Point estimaPosicionFutura(int t){
		return estimaPosicionFutura(velocidad,angulo,t);
	}

	public int heuristica(Point p, int t) {
		// Si es gol nuestro, MAX
		int esGol = esGol((int)p.getX(),(int)p.getY());
		if (esGol != 0)  return esGol*100000;
		else return 0;//return p.x;
	}

	public Point estimaPosicionFutura(int v0, double angulo, int t) {
		int destX,destY;
		int vel = velocidad;
		destX = x;
		destY = y;
		for (int trec=0;trec<t;trec++)
		{
			// calculo iterativo
	        destX+=Math.cos(angulo)*vel;
	        destY+=Math.sin(angulo)*vel;
	        if (esGol(destX,destY)==0){
	            if (x>160){
                    x=320-x;  //160 - (x-160)
                    angulo=General.corregirAngulo(Math.PI-angulo);
                }
                if (x<-160){
                    x=-320-x; //-160 + (-160-x)
                    angulo=General.corregirAngulo(Math.PI-angulo);
                }
                if (y>120){
                    y=240-y; //120 - (x-120)
                    angulo=General.corregirAngulo(-angulo);
                }
                if (y<-120){
                    y=-240-y;  //-120 + (-120-x)
                    angulo=General.corregirAngulo(-angulo);
                }
	        }
	        else break;
	        if (vel>0) vel--;
	        else break;
		}
		return new Point(destX,destY);
	}	
	
    public int esGol(int x, int y){
        if (x>=160 && y>-30 && y<30) return 1;
        if (x<=-160 && y>-30 && y<30) return -1;
        return 0;
    }

	public int esGol() {
		// Si la posicion actual es 0,0 y la anterior estaba a mas de 135
		if (distance(TacticaShalafiBase.centro)==0 && 
				Math.abs(anterior.getX())>135){
			if (this.anterior.getX() > 0) return 1;
			else return -1;
		}
		return 0;
	}
}

@SuppressWarnings("serial") 
class ShalJugador extends ShalJugadorBase {

	/*
	 * Constantes para la heuristica del pase
	 */
	private static final double numIteraciones = 40;
	private static final int KA = 20;
	private static final int KB = 1;
	private static final int KC = -1;

	public enum TipoSeleccionTiro {
		Opuesto, 
		OpuestoMedioCampo, 
		Heuristica, 
		Completo};
	
	protected TipoSeleccionTiro tipoSeleccionTiro; 
	protected TipoMovimiento tipoMovimiento; 
	protected Point inicial;
	protected int radio;
	protected ShalOponente oponenteAlQueCubre;
	protected Point [] pivote = new Point[3];
	protected double calidadesPase[][] = new double[40][6];
	private String nombre = new String("Unamed");
	private boolean repelerEquipo = true;
	private boolean atraerBola = true;
	private double tMin = 10;
	private int direcion;
	
	public ShalJugador (int number, String nombre, int fuerza, int x, int y, TacticaShalafiBase p)
	{
		super (p,number,fuerza);
		this.nombre = nombre;
		this.inicial = new Point(x,y);
		this.tipoSeleccionTiro = TipoSeleccionTiro.OpuestoMedioCampo;
	}

	public void setPosicionInicial(int i, int j) {
		this.inicial.setLocation(i,j);
		
	}

	public void setSeleccionDisparo(TipoSeleccionTiro seleccion) {
		this.tipoSeleccionTiro = seleccion;
	}

	public final void addComandos(Vector<Comando> comandos)
	{
		Point dest;
		dest = getDestino();
		//eps.setPosicionFutura(number,dest);
		comandos.add(new ComandoIrA(number,dest.getX(),dest.getY()));
		// Para el pase solo se mira si esta ceerca de la bola
		double vel = parent.getBola().getVelocidad();
		if (vel < 15 && distance(parent.getBola()) < getVelocidad()+vel+10)
		{
			Point pase = getPase();
			comandos.add(new ComandoGolpearBola(number,pase.getX(),pase.getY()));
		}
	}

	protected final boolean puedoMeterGol()
	{
		//if (eps.getAlcanceMin(getFuerza()) < TacticaShalafiBase.porteriaOponente.distance(x,y)) return true;
		if (TacticaShalafiBase.porteriaOponente.distance(x,y)<this.getAlcanceMinimo()) return true;
		return false;
		
	}

	protected Point getPase()
	{
		if (puedoMeterGol() || deboTirar())
		{
			// Punto de mejor tiro de la porteria ?? 
			return calcularPuntoTiro();
		}
		else
			return mejorPase();
	}
	
	protected Point calcularPuntoTiro() {
		if (y == 0) return new Point(190,0);
		// En la direccion del centro de la porteria
		double angulo = General.getAngulo(x,y,160,0);
		// Calculamos el incremento hasta x = 190
		return new Point(190,(int) (30*Math.sin(angulo)/Math.cos(angulo)));
	}

	protected boolean deboTirar() {
		double dist = TacticaShalafiBase.porteriaOponente.distance(x,y);
		double min, max;
		min = getAlcanceMinimo();
		max = getAlcanceMaximo();
		// El resultado mueve los limites
		int ventaja = parent.getVentaja();
		double inc = (max-min) / 5;
		if (ventaja < 0) min -= ventaja*inc;
		// Solo lo aumentamos, disminuir, no es necesario
		//else max -= ventaja*inc;
		if (dist < min) return true;
		if (dist > max) return false;
		// Linealmente
		double a = 1/(min-max);
		double b = -max*a;
		double temp = dist*a+b; // Valor lineal entre 0 y 1
		// Se podria tener en cuenta mas cosas
		return Math.random()>temp;
	}

	protected Point mejorPase() {
		// Por defecto en direccion contraria a la porteria
		double bestAngulo = General.getAngulo(-160,0,x,y), bestPorcentaje=100;
		if (tipoSeleccionTiro == TipoSeleccionTiro.Opuesto || 
				(tipoSeleccionTiro == TipoSeleccionTiro.OpuestoMedioCampo && x < 0)) 
		{
			if (Math.abs(y)>100) return TacticaShalafiBase.porteriaOponente;
			Point dest = new Point ((int) (x+(bestPorcentaje/2)*Math.cos(bestAngulo)),
					(int) (y+(bestPorcentaje/2)*Math.sin(bestAngulo)));
			return dest;
		}
		if (tipoSeleccionTiro == TipoSeleccionTiro.Completo) 
			return calculaPrecisionTotal();
		else 
			return calculaPrecisionSimple();
	}
	
	protected Point calculaPrecisionSimple()
	{	
		double bestAngulo = General.getAngulo(-160,0,x,y);
		double calidad = 0, real;
		int i;
		double calidadesPase[] = new double[40];
		// Hacemos un barrido radial incremental
		// Tambien barremos por el % de fuerza utilizado
		double incRad = 2*Math.PI/numIteraciones;
		// El pase tiene un error de +- PI/10 nuestras iteraciones son de PI/20
		for (i=0;i<numIteraciones;i++)
		{
			calidadesPase[i] = getCalidadPase (incRad*i, 100);
		}
		// La calidad de un angulo es la media de las 5 posibles i-2, i-1 i i+1 i+2
		// salvo que haya algun gol en propia
		for (i=0;i<numIteraciones;i++)
		{
			real = calidadesPase[i]*10+calidadesPase[(i+1)%40]*2;
			real += 2*calidadesPase[(i+2)%40];
			real += 2*calidadesPase[(i+39)%40];
			real += 2*calidadesPase[(i+38)%40];
			real /= 18;
			if (real > calidad)
			{
				calidad = real;
				bestAngulo = i*incRad;
			}
		}
		Point dest = new Point ((int) (x+60*Math.cos(bestAngulo)),
				(int) (y+60*Math.sin(bestAngulo)));
		return dest;
	}
	
	protected Point calculaPrecisionTotal()
	{	
		double bestAngulo = General.getAngulo(-160,0,x,y), bestPorcentaje=100;
		double calidad = 0, real;
		int i;
		// Hacemos un barrido radial incremental
		// Tambien barremos por el % de fuerza utilizado
		double incRad = 2*Math.PI/numIteraciones;
		// El pase tiene un error de +- PI/10 nuestras iteraciones son de PI/20
		for (i=0;i<numIteraciones;i++)
		{
			// Buscamos la calidad de este angulo
			for (int j=2;j<=5;j++)
			{
				calidadesPase[i][j] = getCalidadPase (incRad*i, j*20);
			}
		}
		// La calidad de un angulo es la peor de las 5 posibles i-2, i-1 i i+1 i+2
		// si el valor central es de casi-gol 900 o + se queda
		for (i=0;i<numIteraciones;i++)
		{
			// Para cada posibilidad de fuerza
			for (int j=2;j<=5;j++)
			{
				if (calidadesPase[i][j] < 8000)
				{
					// Buscamos la calidad de este angulo
					real = Math.min(calidadesPase[i][j],calidadesPase[(i+1)%40][j]);
					real = Math.min(real,calidadesPase[(i+2)%40][j]);
					real = Math.min(real,calidadesPase[(i+39)%40][j]);
					real = Math.min(real,calidadesPase[(i+38)%40][j]);
				}
				else real = calidadesPase[i][j];
				if (real > calidad)
				{
					calidad = real;
					bestAngulo = i*incRad;
					bestPorcentaje = j*20;
				}
			}
		}
		//System.out.println(nombre+" pasa con calidad "+calidad);
		Point dest = new Point ((int) (x+(bestPorcentaje/2)*Math.cos(bestAngulo)),
				(int) (y+(bestPorcentaje/2)*Math.sin(bestAngulo)));
		return dest;
	}

	protected double getCalidadPase(double angulo, int porcentaje) {
		// Vamos incrementando t, cuando tenemos un valor de 2 en intercepcion, break
		double heuristica;
		double a,b,c;
		ShalBola bola = parent.getBola();
		int v0 = getFuerza()*3*porcentaje/100;
		for (int t=0;;t++)
		{
			Point dest = bola.estimaPosicionFutura(v0,angulo,t);			
			a = bola.heuristica(dest,t);
			if (v0 - t < 15)
			{
				b = heuristicaEquipo(parent,t, dest);
				c = parent.getHeuristicaOponentes(t, dest);
				heuristica = KA*a+KB*b+KC*c;
				if ( c>=130 ) break;
			}
			else
			{
				heuristica = KA*a;
				if (Math.abs(a)>10000) return heuristica;
			}
		}
		return heuristica;
	}

	protected double heuristicaEquipo(TacticaShalafiBase eps, int t, Point dest) {
		// El sumatorio de alcanzables
		double res = 0;
		for (int i=0;i<11;i++)
			res += eps.getEquipo(i).getHeuristica(t, dest);
		return res;
	}

	protected Point getDestino()
	{
		Point destino = null;
		if (parent.voyPorLaBola(number))
			destino = parent.getPuntoEncuentroBola();
		else if (tipoMovimiento == TipoMovimiento.ZonaRadial)
			destino = getDestinoZonaRadial();
		else if (tipoMovimiento == TipoMovimiento.Pivote)
			destino = getDestinoPivote();
		else if (tipoMovimiento == TipoMovimiento.Artillero)
			destino = getDestinoArtillero();
		else if (tipoMovimiento == TipoMovimiento.Cubrir)
			destino = getDestinoCubrir();
		else if (tipoMovimiento == TipoMovimiento.Orbital)
			destino = getDestinoOrbital();
		else if (tipoMovimiento == TipoMovimiento.SittingDuck)
			destino = inicial;
		else if (tipoMovimiento == TipoMovimiento.PivoteZonaRadial)
			destino = getDestinoPivoteZonaRadial();
		else 
			destino = getDestinoAntigravedad();
		// Conpobracion de dentro del campo
		if (destino.getX()>156) destino.setLocation(154,destino.getY());
		if (destino.getX()<-156) destino.setLocation(-154,destino.getY());
		if (destino.getY()>116) destino.setLocation(destino.getX(),116);
		if (destino.getY()<-116) destino.setLocation(destino.getX(),-116);
		
		return destino;
	}
	
	protected Point getDestinoPivoteZonaRadial() {
		Point temp = getDestinoPivote();
		temp = correccionCircular(temp);	
		return temp;
	}

	protected Point correccionCircular(Point temp) {
		if (temp.distance(getXInicial(),getYInicial())<radio) return temp;
		else 
		{
			double angulo = General.getAngulo(getXInicial(),getYInicial(),temp.getX(),temp.getY());
			int xdest = (int) (getXInicial() + radio*Math.cos(angulo));
			int ydest = (int) (getYInicial() + radio*Math.sin(angulo));
			return new Point(xdest,ydest);
		}
	}

	protected Point getDestinoCubrir() {
		// Cubres en modo pivote, pero en un radio de peligro*(tuVelocidad-suVelocidad)
		Point temp = getDestinoPivote();
		// radio maximo
		double top = oponenteAlQueCubre.getTiempoMinBola();
		if (top <30)
		{
			double radioMax = oponenteAlQueCubre.getRadioMaximoCubrir();
			//double radioMax = oponenteAlQueCubre.getRadioMaximoCubrir(); //oponenteAlQueCubre.getPeligro()*(getVelocidad()-oponenteAlQueCubre.getVelocidad());
			if (temp.distance(oponenteAlQueCubre) > radioMax)
			{
				double angulo = General.getAngulo(
						oponenteAlQueCubre.getX(),
						oponenteAlQueCubre.getY(),temp.getX(),temp.getY());
				int xdest = (int) (oponenteAlQueCubre.getX() + radioMax*Math.cos(angulo));
				int ydest = (int) (oponenteAlQueCubre.getY() + radioMax*Math.sin(angulo));
				temp.setLocation(xdest,ydest);
			}
		}
		return temp;
	}

	protected Point getDestinoPivote() {
		int destX = (int) ((pivote[0].getX()+pivote[1].getX()+pivote[2].getX())/3);
		int destY = (int) ((pivote[0].getY()+pivote[1].getY()+pivote[2].getY())/3);
		return new Point(destX, destY);
	}

	protected Point getDestinoOrbital()
	{
		//ShalBola bola = parent.getBola();
		// Tomamos al oponente mas cerano
		ShalOponente op = parent.getContrarioMasCerca(this);
		double angulo=General.getAngulo(op.getX(),op.getY(),getX(),getY());
		//double angBola=General.getAngulo(op.getX(),op.getY(),bola.getX(),bola.getY());
		double angPerp;
		// Tener en cuenta que nos tendria que acercar a nuestro centro
		double anguloCentro = General.getAngulo(x,y,getXInicial(),getYInicial());
		// Valor entre 0 y 1 de la separacion del angulo
		double reference = General.corregirAngulo(anguloCentro-angulo)/(2*Math.PI);
		if (reference >0.13 && reference <0.37 && Math.random()>0.4)
			direcion = 1;
		if (reference >0.63 && reference <0.87 && Math.random()>0.4)
			direcion = -1;
		if (direcion == 1)
		{
			angPerp = angulo+Math.PI/2;
		}
		else
		{
			angPerp = angulo-Math.PI/2;
		}
		// Pesamos el alejamiento
		double d = op.distance(x,y);
		double tD = d / op.getVelocidad();
		// Tomamos el valor escalado de la distacia
        double peso = 1-(tD*tD)/(tMin*tMin);
        // Para que fuera bonito seria una gaussiana
        if ((reference <0.13 || reference >0.87) && distance(inicial)>radio)
        {
        	peso += distance(inicial)/radio;
        }
        if (reference > 0.73 && reference < 0.63 && tD > tMin)
        {
        	peso -= distance(inicial)/radio;
        }
        int destX = (int) (getX()+(Math.cos(angPerp)+Math.cos(angulo)*peso)*10);
        int destY = (int) (getY()+(Math.sin(angPerp)+Math.sin(angulo)*peso)*10);		
		return new Point (destX,destY);
	}
	
	protected Point getDestinoArtillero() {
		int alcanceMin = parent.getAlcanceMin(getFuerza());
		int destX = 0, destY = 0;
		Point dest = getDestinoAntigravedad();
		// Chequeo de estar en la zona
		if (dest.getY() < -30)
		{
			if (TacticaShalafiBase.posteOp2.distance(dest) > alcanceMin)
			{
				// El mas cercano del radio
				double angulo = General.getAngulo(getXInicial(),getYInicial(),dest.getX(),dest.getY());
				destX = (int) (TacticaShalafiBase.posteOp2.getX() + alcanceMin*Math.cos(angulo));
				destY = (int) (TacticaShalafiBase.posteOp2.getY() + alcanceMin*Math.sin(angulo));
				dest.move(destX,destY);
			}
		}
		else if (dest.getY() > 30)
		{
			if (TacticaShalafiBase.posteOp1.distance(dest) > alcanceMin)
			{
				// El mas cercano del radio
				double angulo = General.getAngulo(getXInicial(),getYInicial(),dest.getX(),dest.getY());
				destX = (int) (TacticaShalafiBase.posteOp1.getX() + alcanceMin*Math.cos(angulo));
				destY = (int) (TacticaShalafiBase.posteOp1.getY() + alcanceMin*Math.sin(angulo));
				dest.move(destX,destY);
			}
		}
		else
		{
			if (Math.abs(160-dest.getX())>alcanceMin)
			{
				dest.move(160-alcanceMin,(int) dest.getY());
			}
		}
		return dest;			
	}

	protected Point getDestinoZonaRadial() {
		Point bola = parent.getBola();
		return correccionCircular(bola);
	}

	protected Point getDestinoAntigravedad() {
	
		double incX = 0, incY=0; 
		// Repulsion de jugadores
		double d, dD, tCuadrado, tDistancia;
		double aux;
		Point bola = parent.getBola();
		for (int i=0;i<11;i++)
		{
			// Escapar de los oponentes
			ShalOponente temp = parent.getOponente(i);
			d = temp.distance(x,y);
			// Tomamos el valor escalado de la distacia
			tDistancia = d / temp.getVelocidad();
			if (tDistancia<tMin)
			{
				tCuadrado = (tDistancia*tDistancia);
				aux = General.getAngulo(x,y,temp.getX(),temp.getY());
				incX -= 100*Math.cos(aux)/tCuadrado;
				incY -= 100*Math.sin(aux)/tCuadrado;
			}
			if (repelerEquipo)
			{
				double c;
				// No apelotonarse con los de tu mismo equipo
				if (i != number)
				{
					ShalJugador pTemp = parent.getEquipo(i);
					d = pTemp.distance(x,y);
					aux = General.getAngulo(x,y,pTemp.getX(),pTemp.getY());
					dD = d*d;
					if (d<pTemp.getAlcanceMinimo()) c = 2000;
					else if (d >pTemp.getAlcanceMaximo())  c = -2000;
					// Intenta acercarse a la linea de tiro maximo
					else c = -2000 * ((d-pTemp.getAlcanceMinimo())/(pTemp.getAlcanceMaximo()-pTemp.getAlcanceMinimo()));
					incX -= c*Math.cos(aux)/dD;
					incY -= c*Math.sin(aux)/dD;
				}
			}
		}
		// Atraccion hacia la bola
		if (atraerBola)
		{
			d = bola.distance(x,y);
			dD = d*d;
			aux = General.getAngulo(x,y,bola.getX(),bola.getY());
			incX += 300*Math.cos(aux)/dD;
			incY += 300*Math.sin(aux)/dD;
		}
		// Mejor generamos un vector de atraccion hacia el centro
		d = inicial.distance(this)/radio;
		if (d > 0.1)
		{			
			dD = d*d;
			aux = General.getAngulo(x,y,inicial.getX(),inicial.getY());
			incX += 10*dD*Math.cos(aux);
			incY += 10*dD*Math.sin(aux);
		}
		// Calculo final, si el incremento es peque�, pasamos
		
		if (Math.abs(incX) <0.1 && Math.abs(incY) <0.1) 
		{
			//System.out.println("No muevo "+number);
			return this;
		}
		double angulo = General.getAngulo(0,0,incX,incY);
		int destX,destY;
		destX = (int) (x + getVelocidad()*Math.cos(angulo));
		destY = (int) (y + getVelocidad()*Math.sin(angulo));

		if (x>160 || x <-160) destX = (int) inicial.getX();
		if (y>120 || y <-120) destY = (int) inicial.getY();
		Point dest = new Point(destX, destY);
		return dest;
	}

	public final int getXInicial() {		
		return (int) inicial.getX();
	}

	public final int getYInicial() {		
		return (int) inicial.getY();
	}

	public enum TipoMovimiento{
		ZonaRadial,
		Pivote,
		Antigravedad,
		Artillero,
		Cubrir,
		SittingDuck,
		PivoteZonaRadial,
		Orbital
	}

	public void setTipoMovimiento(TipoMovimiento tipo, int r, boolean b1, boolean b2) {
		this.tipoMovimiento = tipo;
		if (tipoMovimiento == TipoMovimiento.Artillero 
				|| tipoMovimiento == TipoMovimiento.Antigravedad)
		{
			repelerEquipo = b1;
			atraerBola = b2;
			radio = r;
		}
	}
	
	public void setTipoMovimiento(TipoMovimiento tipo, int valor) {
		this.tipoMovimiento = tipo;
		if (tipoMovimiento == TipoMovimiento.ZonaRadial 
				|| tipoMovimiento == TipoMovimiento.Antigravedad 
				|| tipoMovimiento ==TipoMovimiento.Artillero)
			this.radio = valor;
		else if (tipoMovimiento == TipoMovimiento.PivoteZonaRadial)
		{
			this.radio = valor;
			pivote[0] = parent.getBola();
			pivote[1] = TacticaShalafiBase.poste1;
			pivote[2] = TacticaShalafiBase.poste2;
		}
		if (tipoMovimiento == TipoMovimiento.Cubrir || tipoMovimiento == TipoMovimiento.Pivote)
		{
			if (tipoMovimiento == TipoMovimiento.Cubrir)
			{
				oponenteAlQueCubre = parent.getOponente(valor);
				oponenteAlQueCubre.setCubiertoPor(this.number);
			}
			pivote[0] = parent.getBola();
			pivote[1] = parent.getEquipo(0);
			pivote[2] = parent.getOponente(valor);
		}
	}

	/* 
	 * Para modo antigravedad o artilleros, nos basta con esta informacion
	 */
	public void setTipoMovimiento(TipoMovimiento tipo) {
		this.tipoMovimiento = tipo;
	}

	public void update(Point p) {
		this.setLocation(p);
		peligro = calculaPeligro();
	}
	
	protected double calculaPeligro (Point p)
	{
		return calculaPeligro(p.distance(TacticaShalafiBase.porteriaOponente));
	}
	
	protected double calculaPeligro ()
	{
		return calculaPeligro(distance(TacticaShalafiBase.porteriaOponente));
	}

	public void setTipoMovimiento(TipoMovimiento tipoMovimiento, ShalOponente oponente) {
		if (tipoMovimiento == TipoMovimiento.Cubrir || tipoMovimiento == TipoMovimiento.Pivote)
		{
			this.tipoMovimiento = tipoMovimiento;
			if (tipoMovimiento == TipoMovimiento.Cubrir)
			{
				oponenteAlQueCubre = oponente;
				oponenteAlQueCubre.setCubiertoPor(this.number);
			}
			pivote[0] = parent.getBola();
			pivote[1] = parent.getEquipo(0);
			pivote[2] = oponente;
		}
	}
	
	public void setTipoMovimiento(TipoMovimiento tipoMovimiento, Point p1, Point p2) {
		if (tipoMovimiento == TipoMovimiento.Pivote)
		{
			this.tipoMovimiento = tipoMovimiento;
			pivote[0] = parent.getEquipo(0);
			pivote[1] = p1;
			pivote[2] = p2;
		}
	}
	
	public void setTipoMovimiento(TipoMovimiento tipoMovimiento, Point p1, Point p2,  Point p3) {
		if (tipoMovimiento == TipoMovimiento.Pivote)
		{
			this.tipoMovimiento = tipoMovimiento;
			pivote[0] = p1;
			pivote[1] = p2;
			pivote[2] = p3;
		}
	}
}

@SuppressWarnings("serial") 
class ShalOponente extends ShalJugadorBase 
{
	private Point anterior;
	private int meCubre;
	
	public ShalOponente(int i,TacticaShalafiBase p) {
		super(p,i,7);
		anterior = null;
	}

	public boolean update(Point nuevo, boolean comprobarVelocidad) {
		boolean ret = false;
		this.setLocation(nuevo);
		if (anterior== null) 
		{
			anterior = new Point(nuevo);
			ret = true;
		}
		if (comprobarVelocidad)
		{
			// Actualizacion de la fuerza
			int temp = (int) Math.floor(anterior.distance(this));
			if (temp <7 && temp > getVelocidad()) {
				fuerza = 10 - temp;
				ret = true;
			}
		}
		// Calculo del peligro
		peligro = calculaPeligro();
		
		return ret;
	}

	public void setCubiertoPor(int i) {
		this.meCubre = i;
	}

	public boolean estaBienCubierto() {
		if (meCubre < 0) return false;
		return distance(parent.getJugador(meCubre)) < getRadioMaximoCubrir();
	}

	public double getRadioMaximoCubrir() {
		//double top = getTiempoMinBola();
		//double radioMax = parent.getEquipo(meCubre).getVelocidad()*top-getVelocidad()*top;
		return 40;
	}

	public Object getQuienCubre() {
		if (meCubre>0)
			return parent.getJugador(meCubre);
		else 
			return null;
	}

	protected double calculaPeligro (Point p)
	{
		return calculaPeligro(p.distance(TacticaShalafiBase.porteria));
	}
	
	protected double calculaPeligro ()
	{
		return calculaPeligro(distance(TacticaShalafiBase.porteria));
	}
}

class DistanceComparator implements Comparator<Point> {

	private Point op;

	public DistanceComparator(Point oponente) {
		this.op = oponente;
	}

	public int compare(Point o1, Point o2) {
		return (int) (op.distance( o1) - op.distance(o2));
	}
}